use std::{
    error::Error,
    fmt,
    fs::File,
    io::Write,
    path::{Path, PathBuf},
};

use aes::{
    cipher::{block_padding::Pkcs7, BlockDecryptMut, KeyIvInit},
    Aes128,
};

use cbc::Decryptor;
use reqwest::Url;
use tokio::sync::Semaphore;

type Aes128CbcDec = Decryptor<Aes128>;

/// hex字符串转Vec<u8>
pub fn hex_string_to_u8_vec(s: &str) -> Vec<u8> {
    let mut bytes = vec![];
    for i in 0..s.len() / 2 {
        let start = i * 2;
        let end = start + 2;
        let byte_str = &s[start..end];
        let byte = u8::from_str_radix(byte_str, 16).expect("invalid hex");
        bytes.push(byte);
    }
    bytes
}
/// 解密   加密网页 https://the-x.cn/zh-cn/cryptography/Aes.aspx [CBC-PKCS7-128-key-iv]
pub fn decrypt(cipher_byte: Vec<u8>, key: &[u8], iv: &[u8]) -> Vec<u8> {
    let mut vec_buf = vec![0u8; cipher_byte.len()];
    vec_buf[..cipher_byte.len()].copy_from_slice(&cipher_byte);
    let pt = Aes128CbcDec::new(key.into(), iv.into())
        .decrypt_padded_b2b_mut::<Pkcs7>(&cipher_byte, &mut vec_buf)
        .unwrap();
    pt.to_vec()
}

pub async fn passwd_paser(
    semaphore: &Semaphore,
    url: Url,   // 链接
    key: &[u8], // key
    iv: String, // IV
    // 保存地址[目录]
    save_path: PathBuf,
) -> Result<(), Box<dyn Error>> {
    // 获取许可证
    let permit = semaphore.acquire().await.unwrap();
    let res = httpgetstream(url.clone()).await?;
    // 解密
    let okres = decrypt(res.to_vec(), key, &hex_string_to_u8_vec(&iv.clone()[2..]));
    // 创建一个文件，如果文件不存在则创建，如果文件已存在则覆盖。
    let ts_name = url.path().split("/").last().unwrap();
    let tspath = save_path.join(ts_name);
    // println!("tspath-->{}",m3path);
    let mut file = File::create(&tspath).unwrap();
    file.write_all(&okres).unwrap();
    // 关闭文件。
    file.sync_all().expect("读写文件❌");
    // 当任务完成时，释放许可证
    drop(permit);
    Ok(())
}

/// No不需要解密下载
pub async fn nopasswd_paser(
    semaphore: &Semaphore,
    url: Url, // TS文件链接
    // 保存地址
    save_path: PathBuf,
) -> Result<(), Box<dyn Error>> {
    // 获取许可证
    let permit = semaphore.acquire().await.unwrap();
    let res = httpgetstream(url.clone()).await?;
    // 创建一个文件，如果文件不存在则创建，如果文件已存在则覆盖。
    let ts_name = url.as_str().split("/").last().unwrap();
    let tspath = save_path.join(ts_name);
    // println!("tspath-->{}",m3path);
    let mut file = File::create(&tspath).unwrap();
    file.write_all(&res).unwrap();
    // 关闭文件。
    file.sync_all().expect("读写文件❌");
    // println!("{} <--> {}", url, tspath);
    // 当任务完成时，释放许可证
    drop(permit);
    // println!("{} has finished", ts_name);
    Ok(())
}

// 定义一个自定义错误类型
#[derive(Debug)]
pub struct HttpError {
    pub message: String,
}

// 实现 Error trait
impl std::error::Error for HttpError {}

// 实现 Display trait 以方便打印错误信息
impl fmt::Display for HttpError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.message)
    }
}

pub async fn httpget(url: Url) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
    let dio = reqwest::Client::new();
    let res = dio.get(url)
        .timeout(std::time::Duration::from_secs(30))
        .header("User-Agent","Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0")
        .send()
        .await?;
    // println!("{}.status(){}", url, res.status());
    if res.status() != reqwest::StatusCode::OK {
        // println!("404");
        // 使用自定义错误
        return Err(Box::new(HttpError {
            message: format!("[HTTP-ERROR]{}", res.status()),
        }));
    }
    // println!("key->{:#?}", res.text().await?);
    let data = res.bytes().await?;
    Ok(data.to_vec())
}

pub async fn httpgetstream(url: Url) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
    let dio = reqwest::Client::new();
    let res = dio.get(url)
        .timeout(std::time::Duration::from_secs(30))
        .header("User-Agent","Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0")
        .header("content-type", "application/octet-stream")
        .send()
        .await?;
    // println!("{}.status(){}", url, res.status());
    if res.status() != reqwest::StatusCode::OK {
        // println!("404");
        // 使用自定义错误
        return Err(Box::new(HttpError {
            message: format!("[HTTP-ERROR]{}", res.status()),
        }));
    }
    // println!("key->{:#?}", res.text().await?);
    let data = res.bytes().await?;
    Ok(data.to_vec())
}
